`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2020/12/14 12:16:36
// Design Name: 
// Module Name: dma
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module dma(
input clk,rstn,set_state,cpu_to_dma_enable,cpu_to_dma_valid,mem_to_dma_enable,mem_to_dma_valid,
input [3:0] mem_data_in,
input [7:0] cpu_data_in,
output reg [3:0] mem_data_out,
output reg [7:0] cpu_data_out,
output reg state,buf1_empty,buf1_full,buf2_empty,buf2_full,buf1_r,buf2_r,
output reg [2:0] buf1_addr,buf2_addr  );

 reg [4:0] buf1_cnt,buf2_cnt;

 reg buf1_readp,buf2_readp,buf1_writep,buf2_writep,buf1_fifo_state,buf2_fifo_state,buf1_ready_to_change,buf2_ready_to_change;
 reg [1:0] next_fifo_state;
 reg [7:0] buf1 [7:0];
 reg [7:0] buf2 [7:0];

initial begin
state=0;
buf1_addr=0;
buf2_addr=0;
buf1_r=0;
buf2_r=0;
buf1_fifo_state=0;
buf2_fifo_state=1;
mem_data_out=4'h0;
cpu_data_out=8'h00;
end
always@(posedge clk)begin
if(set_state && !rstn)
state=~state;
else if(rstn)begin
state=0;
buf1_addr=0;
buf2_addr=0;
buf1_r=0;
buf2_r=0;
buf1_fifo_state=0;
buf2_fifo_state=1;
mem_data_out=4'h0;
cpu_data_out=8'h00;
end
end
 always@(posedge clk)begin
 if(state && !buf1_fifo_state && mem_to_dma_enable && mem_to_dma_valid && !buf1_r)begin
 buf1_writep=1'b1;
 buf1_readp=1'b0;
 buf1[buf1_addr][3:0]<=mem_data_in;
 buf1_r=1;
 end
 else if(state && !buf1_fifo_state && mem_to_dma_enable && mem_to_dma_valid && buf1_r)begin
 buf1_writep=1'b1;
 buf1_readp=1'b0;
 buf1[buf1_addr][7:4]<=mem_data_in;
 buf1_r=0;
 buf1_fifo_state=1;
 buf1_addr=buf1_addr+1;
 end
 else if(state && buf1_fifo_state && cpu_to_dma_enable && cpu_to_dma_valid)begin
 buf1_writep=1'b0;
 buf1_readp=1'b1;
 cpu_data_out<=buf1[buf1_addr];
 buf1_fifo_state=0;
 end
 end
 always@(posedge clk)begin
 if(!state && buf2_fifo_state && mem_to_dma_enable && mem_to_dma_valid && !buf2_r)begin
 buf2_readp=1'b1;
 buf2_writep=1'b0;
 mem_data_out<=buf2[buf2_addr][3:0];
 buf2_r=1;
 end
  else if(!state && buf2_fifo_state && mem_to_dma_enable && mem_to_dma_valid && buf2_r)begin
 buf2_readp=1'b1;
 buf2_writep=1'b0;
 mem_data_out<=buf2[buf2_addr][7:4];
 buf2_r=0;
 buf2_fifo_state=0;
 end
 else if (state && buf2_fifo_state && cpu_to_dma_enable && cpu_to_dma_valid)begin
 buf2_readp=1'b1;
 buf2_writep=1'b0;
 buf2[buf2_addr]<=cpu_data_in;
 buf2_fifo_state=1;
 buf2_addr=buf2_addr+1;
end
end
 always@(posedge clk)begin
 if(!rstn && !state)begin
 case({cpu_to_dma_enable && cpu_to_dma_valid,mem_to_dma_enable && mem_to_dma_valid})
 2'b00:
 buf1_cnt<=buf1_cnt;
 2'b01:
 buf1_cnt<=buf1_cnt-1;
 2'b10:
 buf1_cnt<=buf1_cnt+2;
 2'b11:
 buf1_cnt<=buf1_cnt+1;
 endcase
 end
 else if(!rstn && state)begin
  case({cpu_to_dma_enable && cpu_to_dma_valid,mem_to_dma_enable && mem_to_dma_valid})
 2'b00:
 buf1_cnt<=buf1_cnt;
 2'b01:
 buf1_cnt<=buf1_cnt+1;
 2'b10:
 buf1_cnt<=buf1_cnt-2;
 2'b11:
 buf1_cnt<=buf1_cnt-1;
 endcase
 end
 else buf1_cnt<=5'b00000;
 end
  always@(posedge clk)begin
 if(!rstn)begin
 case({cpu_to_dma_enable && cpu_to_dma_valid,mem_to_dma_enable && mem_to_dma_valid})
 2'b00: buf1_cnt<=buf1_cnt;
 2'b01:
 buf1_cnt<=buf1_cnt+(2*state-1)*buf1_fifo_state;
 2'b10:
 buf1_cnt<=buf1_cnt+(2-4*state)*buf1_fifo_state;
 2'b11: buf1_cnt<=buf1_cnt+(1-2*state)*buf1_fifo_state;
 endcase
 end
 else buf1_cnt<=5'b00000;
 end
 always@(posedge clk)begin
if(!rstn)begin
  case({cpu_to_dma_enable && cpu_to_dma_valid,mem_to_dma_enable && mem_to_dma_valid})
  2'b00:buf2_cnt<=buf2_cnt;
  2'b01:
 buf2_cnt<=buf2_cnt+(2*state-1)*buf2_fifo_state;
 2'b10:
 buf2_cnt<=buf2_cnt+(2-4*state)*buf2_fifo_state;
 2'b11: buf1_cnt<=buf1_cnt+(1-2*state)*buf2_fifo_state;
 endcase
 end
 else buf2_cnt<=5'b00000;
 end
 always @(buf1_cnt) begin
   if (buf1_cnt == 5'b00000)begin
      buf1_empty <= 1'b1;
      buf1_full<=1'b0;
      buf1_fifo_state<=1'b0;
      end
   else if(buf1_cnt>5'b01111)begin
      buf1_empty <= 1'b0;
      buf1_full<=1'b1;
      buf1_fifo_state<=1'b1;
      end
   else begin
   buf1_empty<=1'b0;
   buf1_full<=1'b0;
   buf1_fifo_state<=1'b0;
   end
end

 always @(buf2_cnt) begin
   if (buf2_cnt == 5'b00000)begin
      buf2_empty <= 1'b1;
      buf2_full<=1'b0;
     buf2_fifo_state<=1'b0;
      end
   else if(buf2_cnt>5'b01111)begin
      buf2_empty <= 1'b0;
      buf2_full<=1'b1;
      buf2_fifo_state<=1'b1;
      end
   else begin
   buf2_empty<=1'b0;
   buf2_full<=1'b0;
   buf2_fifo_state<=1'b1;
   end
end
always@(posedge clk) begin
if(buf1_full==state && buf1_empty==!state)begin
buf1_ready_to_change=1'b1;
end
end
always@(posedge clk) begin
if(buf2_full==!state && buf2_empty==state)begin
buf2_ready_to_change=1'b1;
end
end
 always@(posedge clk)begin
 if(buf1_ready_to_change && buf2_ready_to_change)begin
 buf1_fifo_state=~buf1_fifo_state;
 buf2_fifo_state=~buf2_fifo_state;
 buf1_ready_to_change=0;
 buf2_ready_to_change=0;
 end
 end
endmodule
